import { expect } from "@playwright/test";

import { FeaturesRepository } from "@calcom/features/flags/features.repository";
import { PermissionCheckService } from "@calcom/features/pbac/services/permission-check.service";
import { randomString } from "@calcom/lib/random";
import { prisma } from "@calcom/prisma";

import { clearFilters, applySelectFilter } from "./filter-helpers";
import { test } from "./lib/fixtures";
import { createAllPermissionsArray, enablePBACForTeam } from "./lib/test-helpers/pbac";

test.describe.configure({ mode: "parallel" });

const createTeamsAndMembership = async (userIdOne: number, userIdTwo: number) => {
  const teamOne = await prisma.team.create({
    data: {
      name: "test-insights",
      slug: `test-insights-${Date.now()}-${randomString(5)}}`,
    },
  });

  const teamTwo = await prisma.team.create({
    data: {
      name: "test-insights-2",
      slug: `test-insights-2-${Date.now()}-${randomString(5)}}`,
    },
  });
  if (!userIdOne || !userIdTwo || !teamOne || !teamTwo) {
    throw new Error("Failed to create test data");
  }

  // create memberships
  await prisma.membership.create({
    data: {
      userId: userIdOne,
      teamId: teamOne.id,
      accepted: true,
      role: "ADMIN",
    },
  });
  await prisma.membership.create({
    data: {
      teamId: teamTwo.id,
      userId: userIdOne,
      accepted: true,
      role: "ADMIN",
    },
  });
  await prisma.membership.create({
    data: {
      teamId: teamOne.id,
      userId: userIdTwo,
      accepted: true,
      role: "MEMBER",
    },
  });
  await prisma.membership.create({
    data: {
      teamId: teamTwo.id,
      userId: userIdTwo,
      accepted: true,
      role: "MEMBER",
    },
  });
  return { teamOne, teamTwo };
};

test.afterEach(async ({ users }) => {
  await users.deleteAll();
});

test.describe("Insights", async () => {
  test("should be able to go to insights as admins", async ({ page, users }) => {
    const user = await users.create();
    const userTwo = await users.create();
    await createTeamsAndMembership(user.id, userTwo.id);

    await user.apiLogin();

    // go to insights page
    await page.goto("/insights");

    await page.locator('[data-testid^="insights-filters-false-"]').waitFor();
  });

  test("should be able to go to insights as members", async ({ page, users }) => {
    const user = await users.create();
    const userTwo = await users.create();

    await userTwo.apiLogin();

    await createTeamsAndMembership(user.id, userTwo.id);
    // go to insights page
    await page.goto("/insights");

    await page.locator('[data-testid^="insights-filters-false-"]').waitFor();
  });

  test("team select filter should have 2 teams and your account option only as member", async ({
    page,
    users,
  }) => {
    const user = await users.create();
    const userTwo = await users.create();

    await user.apiLogin();

    await createTeamsAndMembership(user.id, userTwo.id);

    // go to insights page
    await page.goto("/insights");
    await page.locator(`[data-testid=insights-filters-false-undefined-${user.id}]`).waitFor();

    await page.getByTestId("dashboard-shell").getByText("Your account").click();

    await expect(await page.locator('[data-testid="org-teams-filter-item"]')).toHaveCount(3);

    await expect(await page.locator('[data-testid="org-teams-filter-item"]', { hasText: "All" })).toHaveCount(
      0
    );

    await expect(
      await page.locator('[data-testid="org-teams-filter-item"]', { hasText: "Your account" })
    ).toHaveCount(1);
  });

  test("Insights Organization should have isAll option true", async ({ users, page }) => {
    const owner = await users.create(undefined, {
      hasTeam: true,
      isUnpublished: true,
      isOrg: true,
      hasSubteam: true,
    });
    await owner.apiLogin();

    await page.goto("/insights");
    await page.locator('[data-testid^="insights-filters-true-"]').waitFor();

    await page.locator('[data-testid^="insights-filters-true-"]').getByText("All").click();

    await expect(await page.locator('[data-testid="org-teams-filter-item"]')).toHaveCount(3);

    await expect(
      await page
        .locator('[data-testid="org-teams-filter-item"]', { hasText: "All" })
        .locator('input[type="checkbox"]')
    ).toBeChecked();
  });

  test("should not have all option as admin in non-org", async ({ page, users }) => {
    const owner = await users.create();
    const member = await users.create();

    await createTeamsAndMembership(owner.id, member.id);

    await owner.apiLogin();

    await page.goto("/insights");

    await page.getByTestId("dashboard-shell").getByText("Your account").click();

    await expect(await page.locator('[data-testid="org-teams-filter-item"]')).toHaveCount(3);

    await expect(await page.locator('[data-testid="org-teams-filter-item"]', { hasText: "All" })).toHaveCount(
      0
    );
  });

  test("should be able to switch between teams and self profile for insights", async ({ page, users }) => {
    const owner = await users.create();
    const member = await users.create();

    await createTeamsAndMembership(owner.id, member.id);

    await owner.apiLogin();

    await page.goto("/insights");

    await page.getByTestId("dashboard-shell").getByText("Your account").click();

    await expect(await page.locator('[data-testid="org-teams-filter-item"]')).toHaveCount(3);

    // switch to self profile
    await page.locator('[data-testid="org-teams-filter-item"]', { hasText: "Your account" }).click();

    // switch to team 1
    await page.locator('[data-testid="org-teams-filter-item"]', { hasText: "test-insights" }).first().click();

    // switch to team 2
    await page.locator('[data-testid="org-teams-filter-item"]', { hasText: "test-insights-2" }).click();
  });

  test("should be able to switch between memberUsers", async ({ page, users }) => {
    const owner = await users.create();
    const member = await users.create();

    await createTeamsAndMembership(owner.id, member.id);

    await owner.apiLogin();

    await page.goto("/insights");

    // Choose a team
    await page.getByTestId("dashboard-shell").getByText("Your account").click();
    await page.locator('[data-testid="org-teams-filter-item"]').nth(1).click();
    await page.keyboard.press("Escape");

    await applySelectFilter(page, "userId", member.username || "");

    await clearFilters(page);

    await expect(page).not.toHaveURL(/[?&]userId=/);
  });

  test("should test download button", async ({ page, users }) => {
    const owner = await users.create();
    const member = await users.create();

    await createTeamsAndMembership(owner.id, member.id);

    await owner.apiLogin();

    await page.goto("/insights");

    const downloadPromise = page.waitForEvent("download");

    // Expect download button to be visible
    await expect(page.locator("text=Download")).toBeVisible();

    // Click on Download button
    await page.getByText("Download").click();

    // Expect as csv option to be visible
    await expect(page.locator("text=as CSV")).toBeVisible();

    // Start waiting for download before clicking. Note no await.
    await page.getByText("as CSV").click();
    const download = await downloadPromise;

    // Wait for the download process to complete and save the downloaded file somewhere.
    await download.saveAs("./" + "test-insights.csv");
  });

  test("should render all ChartCard components with expected titles", async ({ page, users }) => {
    const owner = await users.create();
    const member = await users.create();

    await createTeamsAndMembership(owner.id, member.id);

    await owner.apiLogin();

    await page.goto("/insights");

    const expectedChartTitles = [
      "Events",
      "Performance",
      "Event Trends",
      "Bookings by Hour",
      "Average Event Duration",
      "Most Bookings Scheduled",
      "Least Bookings Scheduled",
      "Most Bookings Completed",
      "Least Bookings Completed",
      "Most Cancelled",
      "Most No-Show",
      "Recent No-Show Guests",
      "Highest Rated",
      "Lowest Rated",
      "Recent Ratings",
      "Popular Events",
    ];

    for (const title of expectedChartTitles) {
      const chartCard = page
        .locator("[data-testid='chart-card'] h2")
        .filter({ hasText: new RegExp(`^${title}$`) });
      await expect(chartCard).toBeVisible();
    }
  });

  test("should be able to access insights page with custom role lacking insights.read permission", async ({
    page,
    users,
  }) => {
    const owner = await users.create(undefined, {
      hasTeam: true,
      isUnpublished: true,
      isOrg: true,
    });

    const userOne = await users.create();
    const userTwo = await users.create();

    const { teamOne } = await createTeamsAndMembership(userOne.id, userTwo.id);

    const orgMembership = await owner.getOrgMembership();
    const orgId = orgMembership.team.id;

    await enablePBACForTeam(orgId);
    await enablePBACForTeam(teamOne.id);

    const permissions = createAllPermissionsArray().filter(
      ({ resource, action }) => !(resource === "insights" && action === "read")
    );

    const customRole = await prisma.role.create({
      data: {
        id: `e2e_no_insights_${orgId}_${Date.now()}`,
        name: "E2E Role Without Insights",
        description: "E2E role for testing - has all permissions except insights.read",
        color: "#dc2626",
        teamId: orgId,
        type: "CUSTOM",
        permissions: {
          create: permissions,
        },
      },
    });

    await prisma.membership.update({
      where: {
        userId_teamId: {
          userId: userOne.id,
          teamId: teamOne.id,
        },
      },
      data: {
        customRoleId: customRole.id,
      },
    });

    const featuresRepository = new FeaturesRepository(prisma);
    const isPBACEnabled = await featuresRepository.checkIfTeamHasFeature(orgId, "pbac");
    expect(isPBACEnabled).toBe(true);

    const permissionService = new PermissionCheckService();
    const hasPermission = await permissionService.checkPermission({
      userId: userOne.id,
      teamId: teamOne.id,
      permission: "insights.read",
      fallbackRoles: [],
    });
    expect(hasPermission).toBe(false);

    await userOne.apiLogin();
    await page.goto("/insights");

    // Verify the user can access the insights page
    await page.locator('[data-testid^="insights-filters-"]').waitFor();
    expect(page.url()).toContain("/insights");
  });
});
